#include "SlicerWidget.h"

#include "vtkCamera.h"
#include "vtkProperty2D.h"
#include "vtkStripper.h"
#include "vtkCursor2D.h"
#include "vtkRenderWindow.h"
#include "vtkAxesActor.h"
#include "vtkArrowSource.h"
#include "vtkOrientationMarkerWidget.h"
#include "vtkWorldPointPicker.h"
#include "vtkObjectFactory.h"
#include "vtkLineSource.h"
#include "vtkCommand.h"
#include "vtkCallbackCommand.h"
#include "vtkLineWidget2.h"
#include "vtkPointWidget.h"
#include "vtkCursor2D.h"
#include "vtkPolyLine.h"
#include "vtkCellArray.h"
#include "vtkPointData.h"

#include <QtGlobal>
#include <QPainter>
#include <QMessageBox>

SlicerWidget::SlicerWidget(VisWidgetType type, QWidget *parent)
{
    m_WidgetType = type;

    vtkSmartPointer<vtkInteractorStyleImage> interactorStyleImage = vtkSmartPointer<vtkInteractorStyleImage>::New();
    GetRenderWindow()->GetInteractor()->SetInteractorStyle(interactorStyleImage);

    m_PointPlacer = PointPlacerPointer::New();
    m_HandleRep = HandleRepPoiter::New();
    m_HandleRep->SetPointPlacer(m_PointPlacer);
    m_HandleRep->GetProperty()->SetColor(1,0,1);
    m_HandleRep->SetCursorShape(CreateCursor());

    m_HandleWidget = HandleWidgetPointer::New();
    m_HandleWidget->SetInteractor(GetRenderWindow()->GetInteractor());
    m_HandleWidget->SetCurrentRenderer(GetRenderWindow()->GetRenderers()->GetFirstRenderer());
    m_HandleWidget->SetRepresentation(m_HandleRep);

    m_HandleCallback = HandleCallbackPointer::New();
    m_HandleCallback->SetWidget(this);
    m_HandleWidget->AddObserver(vtkCommand::EndInteractionEvent, m_HandleCallback);

    vtkSmartPointer<vtkCallbackCommand> leftButtonCallback = vtkSmartPointer<vtkCallbackCommand>::New();
    leftButtonCallback->SetCallback(LeftButtonPressed);
    leftButtonCallback->SetClientData(this);

    GetRenderWindow()->GetInteractor()->AddObserver(vtkCommand::LeftButtonPressEvent, leftButtonCallback);

    m_FinalMatrix = TransformMatrixPointer::New();

    m_GreyLookupTable = LookupTablePointer::New();
    m_GreyLookupTable->SetValueRange(0.0, 1.0); // from black to white
    m_GreyLookupTable->SetSaturationRange(0.0, 0.0); // no color saturation
    m_GreyLookupTable->SetRampToLinear();

    m_GreyImageMapToColors = ImageMapToColorsPointer::New();

    m_GreyResliceMapper = ResliceMapperPointer::New();
    m_GreyResliceMapper->SliceFacesCameraOn();
    m_GreyResliceMapper->SliceAtFocalPointOn();
    m_GreyResliceMapper->BorderOff();

    m_GreyActor = SliceActorPointer::New();
    m_Renderer->AddActor(m_GreyActor);

    m_Renderer->Render();
    m_Camera = m_Renderer->GetActiveCamera();
    m_Camera->ParallelProjectionOn();
}

SlicerWidget::~SlicerWidget()
{}

PolyDataPointer SlicerWidget::CreateCursor()
{
    vtkPoints* points = vtkPoints::New();
    vtkCellArray* lineIndices = vtkCellArray::New();

    /*points->InsertNextPoint(-width()/2, 0, 0);
    points->InsertNextPoint(width()/2, 0, 0);
    points->InsertNextPoint(0, -height()/2, 0);
    points->InsertNextPoint(0, height()/2, 0);*/

    points->InsertNextPoint(-25, 0, 0);
    points->InsertNextPoint(25, 0, 0);
    points->InsertNextPoint(0, -25, 0);
    points->InsertNextPoint(0, 25, 0);

    lineIndices->InsertNextCell(2);
    lineIndices->InsertCellPoint(0);
    lineIndices->InsertCellPoint(1);
    lineIndices->InsertNextCell(2);
    lineIndices->InsertCellPoint(2);
    lineIndices->InsertCellPoint(3);

    vtkSmartPointer<vtkUnsignedCharArray> colors = vtkSmartPointer<vtkUnsignedCharArray>::New();
    colors->SetNumberOfComponents(3);
    colors->SetName("Colors");

    unsigned char red[3] = {255,0,0};
    unsigned char green[3] = {0,255,0};
    unsigned char blue[3] = {0,0,255};

    switch (m_WidgetType)
    {
    case (SLICER_AXIAL):
    {
        colors->InsertNextTupleValue(red);
        colors->InsertNextTupleValue(red);
        colors->InsertNextTupleValue(green);
        colors->InsertNextTupleValue(green);
        break;
    }
    case (SLICER_SAGITTAL):
    {
        colors->InsertNextTupleValue(green);
        colors->InsertNextTupleValue(green);
        colors->InsertNextTupleValue(blue);
        colors->InsertNextTupleValue(blue);
        break;
    }
    case (SLICER_CORONAL):
    {
        colors->InsertNextTupleValue(red);
        colors->InsertNextTupleValue(red);
        colors->InsertNextTupleValue(blue);
        colors->InsertNextTupleValue(blue);
        break;
    }
    }

    vtkPolyData* polyData = vtkPolyData::New();
    polyData->SetPoints(points);
    polyData->SetLines(lineIndices);
    polyData->GetPointData()->SetScalars(colors);

    return polyData;
}

void
SlicerWidget::LeftButtonPressed(vtkObject* object, unsigned long eid, void* clientdata, void *calldata)
{
    SlicerWidget* slicer = static_cast<SlicerWidget*>(clientdata);
    QMouseEvent* event = static_cast<QMouseEvent*>(calldata);

    double position[3] = {event->pos().x(), slicer->height()-event->pos().y(), 0.0};

    slicer->ChangeOwnPoint(position);
}

void
SlicerWidget::ChangeOwnPoint(double* pos)
{
    m_HandleRep->SetDisplayPosition(pos);
    m_HandleWidget->On();

    m_Renderer->SetDisplayPoint(pos[0], pos[1], pos[2]);
    m_Renderer->DisplayToWorld();
    double *world = m_Renderer->GetWorldPoint();
    world[2] = m_Camera->GetFocalPoint()[2];

    double newPos[3];

    switch(m_WidgetType)
    {
    case (SLICER_AXIAL):
    {
        newPos[0] = world[0];
        newPos[1] = world[1];
        newPos[2] = world[2];
        break;
    }
    case (SLICER_SAGITTAL):
    {
        newPos[0] = world[2];
        newPos[1] = world[0];
        newPos[2] = world[1];
        break;
    }
    case (SLICER_CORONAL):
    {
        newPos[0] = world[0];
        newPos[1] = world[2];
        newPos[2] = world[1];
        break;
    }
    }

    emit PointChanged(newPos);

    Update();
}

void
SlicerWidget::ChangePoint(double *pos)
{
    double *focal = m_Camera->GetFocalPoint();
    switch (m_WidgetType)
    {
    case (SLICER_AXIAL):
    {
        m_Camera->SetFocalPoint(focal[0], focal[1], pos[2]);
        m_Renderer->SetWorldPoint(pos[0], pos[1], pos[2], 1.0);
        break;
    }
    case (SLICER_SAGITTAL):
    {
        m_Camera->SetFocalPoint(focal[0], focal[1], pos[0]);
        m_Renderer->SetWorldPoint(pos[1], pos[2], pos[0], 1.0);
        break;
    }
    case (SLICER_CORONAL):
    {
        m_Camera->SetFocalPoint(focal[0], focal[1], pos[1]);
        m_Renderer->SetWorldPoint(pos[0], pos[2], pos[1], 1.0);
        break;
    }
    }

    m_Renderer->WorldToDisplay();
    double *disp = m_Renderer->GetDisplayPoint();

    double position[3] = {disp[0], disp[1], disp[2]};
    m_HandleRep->SetDisplayPosition(position);
    m_HandleWidget->On();

    this->Update();
}

void
SlicerWidget::moveCrosshairs()
{
    double *bounds = m_GreyActor->GetBounds();
    int* size = this->GetRenderWindow()->GetSize();
    m_Renderer->SetDisplayPoint(0, 0, 0);
    m_Renderer->DisplayToWorld();
    double *pos = m_Renderer->GetWorldPoint();

    m_Renderer->SetDisplayPoint(m_HairPosition.x(), m_HairPosition.y(), 0);
    m_Renderer->DisplayToWorld();
    //double *pos = m_Renderer->GetWorldPoint();

    vtkSmartPointer<vtkLineSource> lineSource = vtkSmartPointer<vtkLineSource>::New();
    lineSource->SetPoint1(1, 1, 1);
    lineSource->SetPoint2(size[0], size[1], size[2]);

    QMessageBox msgBox;
    msgBox.setText("Size is "+QString::number(size[0])+" "+QString::number(size[1])+" "+QString::number(size[2]));
    msgBox.exec();

    PolyDataMapperPointer mapper = PolyDataMapperPointer::New();
    mapper->SetInput(lineSource->GetOutput());

    ActorPointer actor = ActorPointer::New();
    actor->SetMapper(mapper);
    actor->GetProperty()->SetColor(1,0,0);
    actor->SetPosition(pos[0], pos[1], bounds[5]);
    actor->SetScale(100);

    m_Renderer->AddActor(actor);


    this->Update();
}

void
SlicerWidget::wheelEvent(QWheelEvent *event)
{
    int numDegrees = event->delta()/8;
    int numSteps = numDegrees/15;

    double *focal = m_Camera->GetFocalPoint();
    m_Camera->SetFocalPoint(focal[0], focal[1], focal[2]+numSteps);

    this->Update();
}

void
SlicerWidget::SetImage(DisplayImageType *inputImage, ImageProperties *imageProperties)
{
    m_GreyDisplayImage = inputImage;

    m_GreyLookupTable->SetRange(imageProperties->range[0], imageProperties->range[1]);
    m_GreyLookupTable->Build();

    m_GreyImageMapToColors->SetLookupTable(m_GreyLookupTable);
    m_GreyImageMapToColors->SetInput(m_GreyDisplayImage);
    m_GreyImageMapToColors->Update();

    m_GreyResliceMapper->SetInput(m_GreyImageMapToColors->GetOutput());

    m_GreyActor->SetUserMatrix(m_FinalMatrix);
    m_GreyActor->SetMapper(m_GreyResliceMapper);

    m_Renderer->ResetCamera();
    int zoom = 3;
    m_Camera->Zoom(zoom);
  //  if (m_WidgetType == SLICER_AXIAL)
  //      m_Camera->SetViewUp(0,-1,0);

    m_Renderer->SetDisplayPoint(10, 10, 0);
    m_Renderer->DisplayToWorld();
    double *pos = m_Renderer->GetWorldPoint();
    double *bounds = m_GreyActor->GetBounds();

    ArrowSourcePointer xArrow = ArrowSourcePointer::New();
    PolyDataMapperPointer xMapper = PolyDataMapperPointer::New();
    ActorPointer xActor = ActorPointer::New();
    xActor->SetScale(100/zoom);
    xActor->SetPosition(pos[0], pos[1], bounds[5]);

    ArrowSourcePointer yArrow = ArrowSourcePointer::New();
    PolyDataMapperPointer yMapper = PolyDataMapperPointer::New();
    ActorPointer yActor = ActorPointer::New();
    yActor->RotateZ(90);
    yActor->SetScale(100/zoom);
    yActor->SetPosition(pos[0], pos[1], bounds[5]);

    xMapper->SetInput(xArrow->GetOutput());
    xActor->SetMapper(xMapper);

    yMapper->SetInput(yArrow->GetOutput());
    yActor->SetMapper(yMapper);

    switch (m_WidgetType)
    {
    case (SLICER_AXIAL):
    {
        xActor->GetProperty()->SetColor(1,0,0);
        yActor->GetProperty()->SetColor(0,1,0);
        break;
    }
    case (SLICER_SAGITTAL):
    {
        xActor->GetProperty()->SetColor(0,1,0);
        yActor->GetProperty()->SetColor(0,0,1);
        break;
    }
    case (SLICER_CORONAL):
    {
        xActor->GetProperty()->SetColor(1,0,0);
        yActor->GetProperty()->SetColor(0,0,1);
        break;
    }
    }

    m_Renderer->AddActor(xActor);
    m_Renderer->AddActor(yActor);



    GetRenderWindow()->GetInteractor()->Start();

    m_Renderer->Render();

    this->Update();
}

void
SlicerWidget::SetVolume(Material *material, DisplayImagePointer volumeImage)
{
    LookupTablePointer RGBLookupTable = LookupTablePointer::New();
    RGBLookupTable->SetNumberOfTableValues(2);
    RGBLookupTable->SetTableValue(0, 0, 0, 0, 0);
    RGBLookupTable->SetTableValue(1, material->GetColor().red()/255,
                                     material->GetColor().green()/255,
                                     material->GetColor().blue()/255);
    RGBLookupTable->SetTableRange(0, material->GetLabel());
    RGBLookupTable->Build();

    ImageMapToColorsPointer RGBImageMapToColors = ImageMapToColorsPointer::New();
    RGBImageMapToColors->SetLookupTable(RGBLookupTable);
    RGBImageMapToColors->SetInput(volumeImage);
    RGBImageMapToColors->Update();

    ResliceMapperPointer RGBResliceMapper = ResliceMapperPointer::New();
    RGBResliceMapper->SliceFacesCameraOn();
    RGBResliceMapper->SliceAtFocalPointOn();
    RGBResliceMapper->BorderOff();
    RGBResliceMapper->SetInput(RGBImageMapToColors->GetOutput());

    SliceActorPointer actor;
    if (m_ActorsMap->value(material->GetLabel()) == NULL)
    {
        actor = SliceActorPointer::New();
        actor->SetUserMatrix(m_FinalMatrix);

        m_ActorsMap->insert(material->GetLabel(), actor.GetPointer());
        m_Renderer->AddActor(static_cast<vtkImageSlice*>(m_ActorsMap->value(material->GetLabel())));
    }
    else
        actor = static_cast<vtkImageSlice*>(m_ActorsMap->value(material->GetLabel()));

    actor->SetMapper(RGBResliceMapper);
    m_Renderer->Render();

    this->Update();
}

void
SlicerWidget::SetTransformMatrix(TransformMatrixType *transformMatrix)
{
    m_TransformMatrix = transformMatrix;
    switch (m_WidgetType)
    {
    case SLICER_AXIAL:
    {
        m_FinalMatrix = m_TransformMatrix;
        break;
    }
    case SLICER_SAGITTAL:
    {
        static double sagittalElements[16] = {
            0, 1, 0, 0,
            0, 0, 1, 0,
            1, 0, 0, 0,
            0, 0, 0, 1 };
        TransformMatrixPointer sagittalMatrix = TransformMatrixPointer::New();
        sagittalMatrix->DeepCopy(sagittalElements);
        vtkMatrix4x4::Multiply4x4(sagittalMatrix, m_TransformMatrix, m_FinalMatrix);
        break;
    }
    case SLICER_CORONAL:
    {
        static double coronalElements[16] = {
            1, 0, 0, 0,
            0, 0, 1, 0,
            0, 1, 0, 0,
            0, 0, 0, 1 };
        vtkSmartPointer<vtkMatrix4x4> coronalMatrix = vtkMatrix4x4::New();
        coronalMatrix->DeepCopy(coronalElements);
        vtkMatrix4x4::Multiply4x4(coronalMatrix, m_TransformMatrix, m_FinalMatrix);
        break;
    }
    default: m_FinalMatrix = m_TransformMatrix;
    }
}





